package com.meeruu.sg.account.core.domain.biz;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.meeruu.common.model.page.PageResult;
import com.meeruu.common.utils.LoggerUtil;
import com.meeruu.common.utils.mapper.OrikaMapperUtils;
import com.meeruu.sg.account.core.api.constant.AccountGroup;
import com.meeruu.sg.account.core.api.constant.AccountStatus;
import com.meeruu.sg.account.core.api.constant.AccountType;
import com.meeruu.sg.account.core.api.constant.AccountUsage;
import com.meeruu.sg.account.core.api.dto.request.query.*;
import com.meeruu.sg.account.core.api.dto.response.*;
import com.meeruu.sg.account.core.api.exception.AccountException;
import com.meeruu.sg.account.core.api.exception.ErrorCode;
import com.meeruu.sg.account.core.domain.mapper.MerchantAccountMapper;
import com.meeruu.sg.account.core.domain.mapper.OrgAccountMapper;
import com.meeruu.sg.account.core.domain.mapper.UserAccountMapper;
import com.meeruu.sg.account.core.domain.model.KanBanResponse;
import com.meeruu.sg.account.core.domain.model.MerchantAccountDO;
import com.meeruu.sg.account.core.domain.model.OrgAccountDO;
import com.meeruu.sg.account.core.domain.model.UserAccountDO;
import com.meeruu.sg.account.core.domain.util.ConvertUtils;
import com.meeruu.sg.account.core.domain.vo.account.WholeAccount;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

/**
 * @Auther: liwu
 * @Date: 2019/5/16 17:53
 * @Description:
 */
@Slf4j
@Service
public class AccountQueryBiz {

    @Autowired
    private AccountCacheBiz accountCacheBiz;

    @Resource
    private UserAccountMapper userAccountMapper;

    @Resource
    private OrgAccountMapper orgAccountMapper;

    @Resource
    private MerchantAccountMapper merchantAccountMapper;


    public List<AccountType> getAccountType(AccountGroup accountGroup) {

        List<AccountType> accountTypes = new ArrayList<>();
        switch (accountGroup) {
            case USER:
                accountTypes.addAll(Arrays.asList(AccountType.BALANCE, AccountType.SHOW_SCORE, AccountType.SHOW_VALUE));

                break;
            case MERCHANT:
                accountTypes.addAll(Arrays.asList(AccountType.BALANCE, AccountType.DEPOSIT));

                break;
            case ORG:
                accountTypes.addAll(Arrays.asList(AccountType.BALANCE, AccountType.DEPOSIT, AccountType.SHOW_SCORE, AccountType.SHOW_VALUE));
                break;
        }

        return accountTypes;
    }

    /**
     * 获取用户有效账户
     *
     * @param accountGroup
     * @param accountType
     * @param userCode
     * @return
     */
    public List<AccountResponseDTO> listAccounts(AccountGroup accountGroup, AccountType accountType, String userCode) {
        List<AccountResponseDTO> response = null;
        switch (accountGroup) {
            case USER:
                List<UserAccountDO> userAccount = userAccountMapper.listByAccountTypeAndCode(userCode, accountType.getCode());
                if (!CollectionUtils.isEmpty(userAccount)) {
                    response = userAccount.stream().map(this::getResponseByUserAccount).collect(Collectors.toList());
                }
                break;
            case MERCHANT:
                List<MerchantAccountDO> merchantAccount = merchantAccountMapper.listByAccountTypeAndCode(userCode, accountType.getCode());
                if (!CollectionUtils.isEmpty(merchantAccount)) {
                    response = merchantAccount.stream().map(this::getResponseByMerchantAccount).collect(Collectors.toList());
                }
                break;
            case ORG:
                List<OrgAccountDO> orgAccount = orgAccountMapper.listByAccountTypeAndCode(accountType.getCode());
                if (!CollectionUtils.isEmpty(orgAccount)) {
                    response = orgAccount.stream().map(this::getResponseByOrgAccount).collect(Collectors.toList());
                }
                break;
        }

        return response;
    }

    public List<AccountResponseDTO> listAccounts(AccountGroup accountGroup, AccountType accountType, List<String> accountIds) {
        List<AccountResponseDTO> response = null;
        switch (accountGroup) {
            case USER:
                List<UserAccountDO> userAccount = userAccountMapper.listByAccountTypeAndAccountIds(accountIds, accountType.getCode());
                if (!CollectionUtils.isEmpty(userAccount)) {
                    response = userAccount.stream().map(this::getResponseByUserAccount).collect(Collectors.toList());
                }
                break;
            case MERCHANT:
                List<MerchantAccountDO> merchantAccount = merchantAccountMapper.listByAccountTypeAndAccountIds(accountIds, accountType.getCode());
                if (!CollectionUtils.isEmpty(merchantAccount)) {
                    response = merchantAccount.stream().map(this::getResponseByMerchantAccount).collect(Collectors.toList());
                }
                break;
            case ORG:
                List<OrgAccountDO> orgAccount = orgAccountMapper.listByAccountTypeAndAccountIds(accountIds, accountType.getCode());
                if (!CollectionUtils.isEmpty(orgAccount)) {
                    response = orgAccount.stream().map(this::getResponseByOrgAccount).collect(Collectors.toList());
                }
                break;
        }

        return response;
    }


    public AccountResponseDTO getAccount(String accountId, AccountGroup accountGroup) {

        AccountResponseDTO response = null;
        switch (accountGroup) {
            case USER:
                UserAccountDO userAccount = userAccountMapper.selectByAccountId(accountId);
                response = this.getResponseByUserAccount(userAccount);

                break;
            case MERCHANT:
                MerchantAccountDO merchantAccount = merchantAccountMapper.selectByAccountId(accountId);
                response = this.getResponseByMerchantAccount(merchantAccount);

                break;
            case ORG:
                OrgAccountDO orgAccount = orgAccountMapper.selectByAccountId(accountId);
                response = this.getResponseByOrgAccount(orgAccount);

                break;
        }

        return response;
    }


    /**
     * 根据userCode、accountType查询用户账户信息
     *
     * @param userCode    用户唯一code
     * @param accountType 账户类型
     */
    public UserAccountResponseDTO getUserAccountByUserCodeAndAccountType(String userCode, AccountType accountType) {
        UserAccountDO userAccount = accountCacheBiz.getUserAccountCache(userCode, accountType);
        return this.getResponseByUserAccount(userAccount);
    }

    /**
     * 根据merchantCode、accountType查询商家账户信息
     *
     * @param merchantCode 供应商唯一code
     * @param accountType  账户类型
     */
    public MerchantAccountResponseDTO getMerchantAccountByMerchantCodeAndAccountType(String merchantCode, AccountType accountType) {
        MerchantAccountDO merchantAccount = merchantAccountMapper.selectByMerchantCodeAndType(merchantCode, accountType.getCode());
        MerchantAccountResponseDTO merchantResponse = this.getResponseByMerchantAccount(merchantAccount);
        return merchantResponse;
    }


    public PageResult<WholeAccountResponseDTO> getAccountPage(AccountQueryFilterDTO request) {

        PageResult<WholeAccount> tmpResult;

        if (request instanceof UserAccountQueryFilterDTO) {
            LoggerUtil.info(log, "个人账户请求类型" + JSON.toJSONString(request));
            tmpResult = this.getUserAccountList(request);

        } else if (request instanceof OrgAccountQueryFilterDTO) {
            LoggerUtil.info(log, "机构账户请求类型" + JSON.toJSONString(request));
            tmpResult = this.getOrgAccountList(request);

        } else if (request instanceof MerchantAccountQueryFilterDTO) {
            LoggerUtil.info(log, "商户账户请求类型" + JSON.toJSONString(request));
            tmpResult = this.getMerchantAccountList(request);

        } else {
            LoggerUtil.error(log, "没有找到对应的账户请求类型" + JSON.toJSONString(request));
            throw new AccountException(ErrorCode.BIZ_ACCOUNT_REQUEST_NOT_EXIST.getCode(), ErrorCode.BIZ_ACCOUNT_REQUEST_NOT_EXIST.getMessage());
        }

        List<WholeAccount> wholeAccountList = new CopyOnWriteArrayList<>(tmpResult.getData());
        List<WholeAccountResponseDTO> dtos = new CopyOnWriteArrayList<>();
        for (WholeAccount wholeAccount : wholeAccountList) {
            WholeAccountResponseDTO dto = ConvertUtils.convert(wholeAccount, WholeAccountResponseDTO.class);
            dto.setAccountType(AccountType.getTypeByCode(wholeAccount.getAccountType()));
            dto.setAccountStatus(AccountStatus.getStatusByCode(wholeAccount.getStatus()));
            dto.setAccountUsage(AccountUsage.getUsageByCode(wholeAccount.getAccountUsage()));
            dtos.add(dto);
        }

        PageResult<WholeAccountResponseDTO> result = new PageResult<>(tmpResult.getCurrentPage(), tmpResult.getPageSize(), tmpResult.getTotalNum());
        result.setData(dtos);
        return result;

    }


    /*获取User账户列表*/
    private PageResult<WholeAccount> getUserAccountList(AccountQueryFilterDTO request) {

        PageHelper.startPage(request.getPage(), request.getSize());

        UserAccountQueryFilterDTO filterDTO = (UserAccountQueryFilterDTO) request;
        UserAccountDO query = UserAccountDO.builder().build();
        if (request.getAccountType() != null) {
            query.setAccountType(request.getAccountType().getCode());
        }
        if (request.getAccountStatus() != null) {
            query.setStatus(request.getAccountStatus().getCode());
        }
        if (StringUtils.isNotBlank(filterDTO.getUserCode())) {
            query.setUserCode(filterDTO.getUserCode());
        }

        if (StringUtils.isNotBlank(filterDTO.getAccountName())) {
            query.setAccountName(filterDTO.getAccountName());
        }

        Date startTime = null;
        if (request.getOpenStartDate() != null) {
            startTime = request.getOpenStartDate();
        }

        Date endTime = null;
        if (request.getOpenEndDate() != null) {
            endTime = request.getOpenEndDate();
        }
        List<UserAccountDO> accountDOS = userAccountMapper.selectByExample(query, startTime, endTime);
        List<WholeAccount> wholeAccounts = OrikaMapperUtils.mapList(accountDOS, UserAccountDO.class, WholeAccount.class);

        Page pages = (Page) accountDOS;
        PageResult<WholeAccount> pageResult = new PageResult<>(request.getPage(), request.getSize(), pages.getTotal());
        pageResult.setData(wholeAccounts);

        return pageResult;

    }

    /*获取机构账户列表*/
    private PageResult<WholeAccount> getOrgAccountList(AccountQueryFilterDTO request) {

        PageHelper.startPage(request.getPage(), request.getSize());

        OrgAccountQueryFilterDTO filterDTO = (OrgAccountQueryFilterDTO) request;
        OrgAccountDO query = OrgAccountDO.builder().build();
        if (request.getAccountType() != null) {
            query.setAccountType(request.getAccountType().getCode());
        }
        if (StringUtils.isNotBlank(filterDTO.getAccountName())) {
            query.setAccountName(filterDTO.getAccountName());
        }
        if (filterDTO.getAccountUsage() != null) {
            query.setAccountUsage(filterDTO.getAccountUsage().getCode());
        }
        if (request.getAccountStatus() != null) {
            query.setStatus(request.getAccountStatus().getCode());
        }
        if (request.getAccountId() != null) {
            query.setAccountId(request.getAccountId());
        }

        Date startTime = null;
        if (request.getOpenStartDate() != null) {
            startTime = request.getOpenStartDate();
        }

        Date endTime = null;
        if (request.getOpenEndDate() != null) {
            endTime = request.getOpenEndDate();
        }
        List<OrgAccountDO> accountDOS = orgAccountMapper.selectByExample(query, startTime, endTime);
        List<WholeAccount> wholeAccounts = OrikaMapperUtils.mapList(accountDOS, OrgAccountDO.class, WholeAccount.class);

        Page pages = (Page) accountDOS;
        PageResult<WholeAccount> pageResult = new PageResult<>(request.getPage(), request.getSize(), pages.getTotal());
        pageResult.setData(wholeAccounts);

        return pageResult;

    }

    /*获取商铺账户列表*/
    private PageResult<WholeAccount> getMerchantAccountList(AccountQueryFilterDTO request) {
        PageHelper.startPage(request.getPage(), request.getSize());

        MerchantAccountQueryFilterDTO filterDTO = (MerchantAccountQueryFilterDTO) request;
        MerchantAccountDO query = MerchantAccountDO.builder().build();
        if (request.getAccountType() != null) {
            query.setAccountType(request.getAccountType().getCode());
        }
        if (StringUtils.isNotBlank(filterDTO.getAccountName())) {
            query.setAccountName(filterDTO.getAccountName());
        }

        if (StringUtils.isNotBlank(filterDTO.getMerchantName())) {
            query.setMerchantName(filterDTO.getMerchantName());
        }
        if (filterDTO.getAccountUsage() != null) {
            query.setAccountUsage(filterDTO.getAccountUsage().getCode());
        }
        if (request.getAccountStatus() != null) {
            query.setStatus(request.getAccountStatus().getCode());
        }
        if (StringUtils.isNotBlank(filterDTO.getMerchantCode())) {
            query.setMerchantCode(filterDTO.getMerchantCode());
        }

        Date startTime = null;
        if (request.getOpenStartDate() != null) {
            startTime = request.getOpenStartDate();
        }

        Date endTime = null;
        if (request.getOpenEndDate() != null) {
            endTime = request.getOpenEndDate();
        }

        List<MerchantAccountDO> accountDOS = merchantAccountMapper.selectByExample(query, startTime, endTime);
        List<WholeAccount> wholeAccounts = OrikaMapperUtils.mapList(accountDOS, MerchantAccountDO.class, WholeAccount.class);

        Page pages = (Page) accountDOS;
        PageResult<WholeAccount> pageResult = new PageResult<>(request.getPage(), request.getSize(), pages.getTotal());
        pageResult.setData(wholeAccounts);

        return pageResult;

    }

    /**
     * userAccount -> UserAccountResponseDTO
     */
    private UserAccountResponseDTO getResponseByUserAccount(UserAccountDO userAccount) {
        UserAccountResponseDTO response = null;
        if (null != userAccount) {
            response = ConvertUtils.convert(userAccount, UserAccountResponseDTO.class);
            response.setAccountType(AccountType.getTypeByCode(userAccount.getAccountType()));
            response.setAccountStatus(AccountStatus.getStatusByCode(userAccount.getStatus()));
        }
        return response;
    }


    /**
     * merchantAccount -> MerchantAccountResponseDTO
     */
    private MerchantAccountResponseDTO getResponseByMerchantAccount(MerchantAccountDO merchantAccount) {
        MerchantAccountResponseDTO response = null;
        if (null != merchantAccount) {
            response = ConvertUtils.convert(merchantAccount, MerchantAccountResponseDTO.class);
            response.setAccountType(AccountType.getTypeByCode(merchantAccount.getAccountType()));
            response.setAccountStatus(AccountStatus.getStatusByCode(merchantAccount.getStatus()));
        }
        return response;
    }


    /**
     * orgAccount -> OrgAccountResponseDTO
     */
    private OrgAccountResponseDTO getResponseByOrgAccount(OrgAccountDO orgAccount) {
        OrgAccountResponseDTO response = null;
        if (null != orgAccount) {
            response = ConvertUtils.convert(orgAccount, OrgAccountResponseDTO.class);
            response.setAccountType(AccountType.getTypeByCode(orgAccount.getAccountType()));
            response.setAccountStatus(AccountStatus.getStatusByCode(orgAccount.getStatus()));
        }
        return response;
    }


    /**
     * 根据账户名称精确查找机构账户
     *
     * @param name 账户名称
     */
    public OrgAccountResponseDTO getOrgAccountByName(String name) {
        OrgAccountResponseDTO responseDTO = null;
        OrgAccountDO existOrgAccount = orgAccountMapper.selectByAccountName(name.trim());
        if (null != existOrgAccount) {
            responseDTO = ConvertUtils.convert(existOrgAccount, OrgAccountResponseDTO.class);
            responseDTO.setAccountType(AccountType.getTypeByCode(existOrgAccount.getAccountType()));
            responseDTO.setAccountStatus(AccountStatus.getStatusByCode(existOrgAccount.getStatus()));
            responseDTO.setAccountUsage(AccountUsage.getUsageByCode(existOrgAccount.getAccountUsage()));
        }
        return responseDTO;
    }

    public List<UserAccountResponseDTO> getUserAccountGroup(List<UserAccountQueryDTO> queryDTOS) {

        List<UserAccountDO> result = new ArrayList<>();
        Map<AccountType, List<UserAccountQueryDTO>> accountType2User = queryDTOS.stream().collect(Collectors.groupingBy(UserAccountQueryDTO::getAccountType));
        accountType2User.forEach((k, v) -> {
            Integer code = k.getCode();
            List<String> userCodes = v.stream().map(UserAccountQueryDTO::getUserCode).collect(Collectors.toList());
            result.addAll(userAccountMapper.selectByUserCodeGroupAndType(userCodes, code));
        });

        return result.stream().map(this::getResponseByUserAccount).collect(Collectors.toList());
    }


    public List<KanBanResponseDTO> kanbanStatistics(AccountGroup accountGroup) {

        List<KanBanResponse> result = null;
        switch (accountGroup) {
            case USER:
                result = userAccountMapper.kanbanStatistics();
                break;
            case ORG:
                result = orgAccountMapper.kanbanStatistics();
                break;
            case MERCHANT:
                result = merchantAccountMapper.kanbanStatistics();
                break;
            default:
                break;
        }
        return OrikaMapperUtils.mapList(result, KanBanResponse.class, KanBanResponseDTO.class);
    }
}
